#include "Cache.h"

Cache::Cache(unsigned size, unsigned lineSize, unsigned adressSize) :
	size(size), cacheLineSize(lineSize), adressSize(adressSize)
{
}

Cache::~Cache()
{
}

void Cache::performCacheOperation(Parser::TraceElement& element, unsigned& hitCounter, unsigned& missCounter) 
{
	switch(element.type)
	{
	case Parser::TraceType::I: //data read
		performCacheOperation(
			element.adress,
			element.size,
			&Cache::read,
			hitCounter,
			missCounter
			); 
		break;
	case Parser::TraceType::L: //data load
		performCacheOperation(
			element.adress,
			element.size,
			&Cache::load,
			hitCounter,
			missCounter
			); 
		break;
	case Parser::TraceType::S: //data store
		performCacheOperation(
			element.adress,
			element.size,
			&Cache::store,
			hitCounter,
			missCounter
			);
		break;
	case Parser::TraceType::M: //data modify
		performCacheOperation(
			element.adress,
			element.size,
			&Cache::modify,
			hitCounter,
			missCounter
			); 
		break;
	}
}

void Cache::performCacheOperation(unsigned adress, unsigned operationSize, void (Cache::*function)(unsigned, unsigned), unsigned& hitCounter, unsigned& missCounter) 
{
	//read/load/modify/store
	(this->*function)(adress, operationSize);

	//calculates tag, offset and index and stores it in member variables
	calculateAdress(adress);

	//checks if current operation is a hit
	if(checkHit())
		hitCounter++;
	else
		missCounter++;

	int consumedBytes = getConsumedBytes();

	//checks if current operation size exceeds the current cache line
	if(consumedBytes < (int)operationSize)
	{
		performCacheOperation(adress + consumedBytes, operationSize - consumedBytes, function, hitCounter, missCounter);
	}
}

void Cache::read(unsigned adress, unsigned size) 
{
	// for additional read logic
}
void Cache::store(unsigned adress, unsigned size) 
{
	// for additional store logic
}
void Cache::load(unsigned adress, unsigned size) 
{
	// for additional load logic
}
void Cache::modify(unsigned adress, unsigned size)
{
	// for additional modify logic
}
	
int Cache::log2(int x)
{
	return (int)std::ceil(std::log((double)x) / std::log((double)2));
}

void Cache::calculateAdress(unsigned adress)
{
	currentTag = adress >> (offsetBits + indexBits);

	currentIndex = adress >> offsetBits;
	int indexBitMask = (int)std::pow(2.0f, (int)indexBits) - 1;
	currentIndex = currentIndex & indexBitMask;

	int offsetBitMask = (int)std::pow(2.0f, (int)offsetBits) - 1;
	currentOffset = adress & offsetBitMask;
}

int Cache::getConsumedBytes()
{
	return cacheLineSize - currentOffset;
}